#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>

namespace WCMM{

class CRegKey
{
public:
	CRegKey() throw();
	CRegKey(_Inout_ CRegKey& key) throw();
	explicit CRegKey(_In_ HKEY hKey) throw();
	~CRegKey() throw();

	CRegKey& operator=(_Inout_ CRegKey& key) throw();
	operator HKEY() const throw();

	//
	// Attributes
	//

public:
	HKEY m_hKey;
	REGSAM m_samWOW64;

	//
	// Operations
	//

public:

	LONG SetValue(
		_In_ DWORD dwValue,
		_In_opt_z_ LPCTSTR lpszValueName);

	LONG SetValue(
		_In_z_ LPCTSTR lpszValue,
		_In_opt_z_ LPCTSTR lpszValueName = NULL,
		_In_ bool bMulti = false,
		_In_ int nValueLen = -1);

	LONG SetValue(
		_In_opt_z_ LPCTSTR pszValueName,
		_In_ DWORD dwType,
		_In_opt_ const void* pValue,
		_In_ ULONG nBytes) throw();

// 	LONG SetGUIDValue(
// 		_In_opt_z_ LPCTSTR pszValueName,
// 		_In_ REFGUID guidValue) throw();

	LONG SetBinaryValue(
		_In_opt_z_ LPCTSTR pszValueName,
		_In_opt_ const void* pValue,
		_In_ ULONG nBytes) throw();

	LONG SetDWORDValue(
		_In_opt_z_ LPCTSTR pszValueName,
		_In_ DWORD dwValue) throw();

	LONG SetQWORDValue(
		_In_opt_z_ LPCTSTR pszValueName,
		_In_ ULONGLONG qwValue) throw();

	LONG SetStringValue(
		_In_opt_z_ LPCTSTR pszValueName,
		_In_opt_z_ LPCTSTR pszValue,
		_In_ DWORD dwType = REG_SZ) throw();

	LONG SetMultiStringValue(
		_In_opt_z_ LPCTSTR pszValueName,
		_In_z_ LPCTSTR pszValue) throw();

	LONG QueryValue(
		_Out_ DWORD& dwValue,
		_In_opt_z_ LPCTSTR lpszValueName);

	LONG QueryValue(
		_Out_writes_to_opt_(*pdwCount, *pdwCount) LPTSTR szValue,
		_In_opt_z_ LPCTSTR lpszValueName,
		_Inout_ DWORD* pdwCount);

	LONG QueryValue(
		_In_opt_z_ LPCTSTR pszValueName,
		_Out_opt_ DWORD* pdwType,
		_Out_opt_ void* pData,
		_Inout_ ULONG* pnBytes) throw();
	
	//
	// TODO QueryGUIDValue
	//
	
// 	LONG QueryGUIDValue(
// 		_In_opt_z_ LPCTSTR pszValueName,
// 		_Out_ GUID& guidValue) throw();

	LONG QueryBinaryValue(
		_In_opt_z_ LPCTSTR pszValueName,
		_Out_opt_ void* pValue,
		_Inout_opt_ ULONG* pnBytes) throw();

	LONG QueryDWORDValue(
		_In_opt_z_ LPCTSTR pszValueName,
		_Out_ DWORD& dwValue) throw();

	LONG QueryQWORDValue(
		_In_opt_z_ LPCTSTR pszValueName,
		_Out_ ULONGLONG& qwValue) throw();

	LONG QueryStringValue(
		_In_opt_z_ LPCTSTR pszValueName,
		_Out_writes_to_opt_(*pnChars, *pnChars) LPTSTR pszValue,
		_Inout_ ULONG* pnChars) throw();

	LONG QueryMultiStringValue(
		_In_opt_z_ LPCTSTR pszValueName,
		_Out_writes_to_opt_(*pnChars, *pnChars) LPTSTR pszValue,
		_Inout_ ULONG* pnChars) throw();

	//
	// Get the key's security attributes.
	//

	LONG GetKeySecurity(
		_In_ SECURITY_INFORMATION si,
		_Out_opt_ PSECURITY_DESCRIPTOR psd,
		_Inout_ LPDWORD pnBytes) throw();

	//
	// Set the key's security attributes.
	//

	LONG SetKeySecurity(
		_In_ SECURITY_INFORMATION si,
		_In_ PSECURITY_DESCRIPTOR psd) throw();

	LONG SetKeyValue(
		_In_z_ LPCTSTR lpszKeyName,
		_In_opt_z_ LPCTSTR lpszValue,
		_In_opt_z_ LPCTSTR lpszValueName = NULL) throw();

	static LONG WINAPI SetValue(
		_In_ HKEY hKeyParent,
		_In_z_ LPCTSTR lpszKeyName,
		_In_opt_z_ LPCTSTR lpszValue,
		_In_opt_z_ LPCTSTR lpszValueName = NULL);

	//
	// Create a new registry key (or open an existing one).
	//

	LONG Create(
		_In_ HKEY hKeyParent,
		_In_z_ LPCTSTR lpszKeyName,
		_In_opt_z_ LPTSTR lpszClass = REG_NONE,
		_In_ DWORD dwOptions = REG_OPTION_NON_VOLATILE,
		_In_ REGSAM samDesired = KEY_READ | KEY_WRITE,
		_In_opt_ LPSECURITY_ATTRIBUTES lpSecAttr = NULL,
		_Out_opt_ LPDWORD lpdwDisposition = NULL) throw();

	//
	// Open an existing registry key.
	//

	LONG Open(
		_In_ HKEY hKeyParent,
		_In_opt_z_ LPCTSTR lpszKeyName,
		_In_ REGSAM samDesired = KEY_READ | KEY_WRITE) throw();

	//
	// Close the registry key.
	//

	LONG Close() throw();

	//
	// Flush the key's data to disk.
	//

	LONG Flush() throw();

	//
	// Detach the CRegKey object from its HKEY.  Releases ownership.
	//

	HKEY Detach() throw();
	
	//
	// Attach the CRegKey object to an existing HKEY.  Takes ownership.
	//

	void Attach(_In_ HKEY hKey) throw();

	//
	// Enumerate the subkeys of the key.
	//

	LONG EnumKey(
		_In_ DWORD iIndex,
		_Out_writes_to_(*pnNameLength, *pnNameLength) _Post_z_ LPTSTR pszName,
		_Inout_ LPDWORD pnNameLength,
		_Out_opt_ FILETIME* pftLastWriteTime = NULL) throw();

	LONG NotifyChangeKeyValue(
		_In_ BOOL bWatchSubtree,
		_In_ DWORD dwNotifyFilter,
		_In_ HANDLE hEvent,
		_In_ BOOL bAsync = TRUE) throw();

	LONG DeleteSubKey(_In_z_ LPCTSTR lpszSubKey) throw();
	LONG RecurseDeleteKey(_In_z_ LPCTSTR lpszKey) throw();
	LONG DeleteValue(_In_z_ LPCTSTR lpszValue) throw();
};

}